package com.ebupt.migu.fineoperationactivitymonitor.common.util;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Random;
import java.util.stream.IntStream;

import javax.imageio.ImageIO;

public class VerifyCodeUtils {

    /**
     * 使用到Algerian字体，系统里没有的话需要安装字体，字体只显示大写，去掉了1,0,i,o几个容易混淆的字符
     */
    public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
    private static Random random = new Random();

    /**
     * 使用系统默认字符源生成验证码
     *
     * @param verifySize 验证码长度
     * @return
     */
    public static String generateVerifyCode(int verifySize) {
        return generateVerifyCode(verifySize, VERIFY_CODES);
    }

    /**
     * 使用指定源生成验证码
     *
     * @param verifySize 验证码长度
     * @param sources 验证码字符源
     * @return
     */
    public static String generateVerifyCode(int verifySize, String sources) {
        if (sources == null || sources.length() == 0) {
            sources = VERIFY_CODES;
        }
        int codesLen = sources.length();
        Random rand = new Random(System.currentTimeMillis());
        StringBuilder verifyCode = new StringBuilder(verifySize);
        for (int i = 0; i < verifySize; i++) {
            verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
        }
        return verifyCode.toString();
    }

    /**
     * 生成随机验证码文件,并返回验证码值
     *
     * @param w
     * @param h
     * @param outputFile
     * @param verifySize
     * @return
     * @throws IOException
     */
    public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException {
        String verifyCode = generateVerifyCode(verifySize);
        outputImage(w, h, outputFile, verifyCode);
        return verifyCode;
    }

    /**
     * 输出随机验证码图片流,并返回验证码值
     *
     * @param w
     * @param h
     * @param os
     * @param verifySize
     * @return
     * @throws IOException
     */
    public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException {
        String verifyCode = generateVerifyCode(verifySize);
        outputImage(w, h, os, verifyCode);
        return verifyCode;
    }

    /**
     * 生成指定验证码图像文件
     *
     * @param w
     * @param h
     * @param outputFile
     * @param code
     * @throws IOException
     */
    public static void outputImage(int w, int h, File outputFile, String code) throws IOException {
        if (outputFile == null) {
            return;
        }
        File dir = outputFile.getParentFile();
        if (!dir.exists()) {
            dir.mkdirs();
        }
        FileOutputStream fos = null;
        try {
            outputFile.createNewFile();
            fos = new FileOutputStream(outputFile);
            outputImage(w, h, fos, code);
        } finally {
            if (fos != null) {
                fos.close();
            }
        }
    }

    /**
     * 输出指定验证码图片流
     *
     * @param w
     * @param h
     * @param os
     * @param code
     * @throws IOException
     */
    public static void outputImage(int w, int h, OutputStream os, String code) throws IOException {
        int verifySize = code.length();
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Random rand = new Random();
        Graphics2D g2 = image.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Color[] colors = new Color[5];
        Color[] colorSpaces = new Color[] {Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA,
            Color.ORANGE, Color.PINK, Color.YELLOW};
        float[] fractions = new float[colors.length];
        IntStream.range(0, colors.length).forEach(i -> {
            colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
            fractions[i] = rand.nextFloat();
        });
        Arrays.sort(fractions);
        // 设置边框色
        g2.setColor(Color.GRAY);
        g2.fillRect(0, 0, w, h);

        Color c = getRandColor(200, 250);
        // 设置背景色
        g2.setColor(c);
        g2.fillRect(0, 2, w, h - 4);

        // 绘制干扰线
        Random random = new Random();
        // 设置线条的颜色
        g2.setColor(getRandColor(160, 200));
        IntStream.range(0, 20).map(i -> random.nextInt(w - 1)).forEach(x -> {
            int y = random.nextInt(h - 1);
            int xl = random.nextInt(6) + 1;
            int yl = random.nextInt(12) + 1;
            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
        });

        // 添加噪点 噪声率
        float yawpRate = 0.05f;
        int area = (int)(yawpRate * w * h);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(w);
            int y = random.nextInt(h);
            int rgb = getRandomIntColor();
            image.setRGB(x, y, rgb);
        }
        // 使图片扭曲
        shear(g2, w, h, c);

        g2.setColor(getRandColor(100, 160));
        int fontSize = h - 4;
        Font font = new Font("Algerian", Font.ITALIC, fontSize);
        g2.setFont(font);
        char[] chars = code.toCharArray();
        IntStream.range(0, verifySize).forEach(i -> {
            AffineTransform affine = new AffineTransform();
            affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
                (w / verifySize) * i + fontSize / 2, h / 2);
            g2.setTransform(affine);
            g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
        });
        g2.dispose();
        ImageIO.write(image, "png", os);
    }

    private static Color getRandColor(int fc, int bc) {
        if (fc > 255) {
            fc = 255;
        }
        if (bc > 255) {
            bc = 255;
        }
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    private static int getRandomIntColor() {
        int[] rgb = getRandomRgb();
        int color = 0;
        for (int c : rgb) {
            color = color << 8;
            color = color | c;
        }
        return color;
    }

    private static int[] getRandomRgb() {
        return IntStream.range(0, 3).map(i -> random.nextInt(255)).toArray();
    }

    private static void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private static void shearX(Graphics g, int w1, int h1, Color color) {
        int period = random.nextInt(2);
        int frames = 1;
        int phase = random.nextInt(2);
        IntStream.range(0, h1).forEach(i -> {
            double d = (double)(period >> 1)
                * Math.sin((double)i / (double)period + (6.2831853071795862D * (double)phase) / (double)frames);
            g.copyArea(0, i, w1, 1, (int)d, 0);
            g.setColor(color);
            g.drawLine((int)d, i, 0, i);
            g.drawLine((int)d + w1, i, w1, i);
        });

    }

    /**
     * @param g
     * @param w1
     * @param h1
     * @param color
     */
    private static void shearY(Graphics g, int w1, int h1, Color color) {
        int period = random.nextInt(40) + 10;
        int frames = 20;
        int phase = 7;
        IntStream.range(0, w1).forEach(i -> {
            double d = (double)(period >> 1)
                * Math.sin((double)i / (double)period + (6.2831853071795862D * (double)phase) / (double)frames);
            g.copyArea(i, 0, 1, h1, 0, (int)d);
            g.setColor(color);
            g.drawLine(i, (int)d, i, 0);
            g.drawLine(i, (int)d + h1, i, h1);
        });
    }

    /**
     * 根据str,font的样式以及输出文件目录
     *
     * @param str
     * @param font
     * @param width
     * @param height
     * @return
     * @throws Exception
     */
    public static String createImageBASE64(String str, Font font, Integer width, Integer height) throws Exception {
        // 创建图片
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics g = image.getGraphics();
        g.setClip(0, 0, width, height);
        g.setColor(new Color(247, 247, 247));
        // 先用黑色填充整张图片,也就是背景
        g.fillRect(0, 0, width, height);
        // 在换成黑色
        // g.setColor(new Color(153, 153, 153));
        // 修改字体颜色
        g.setColor(new Color(0, 0, 0));
        // 设置画笔字体
        g.setFont(font);
        // 用于获得垂直居中y
        Rectangle clip = g.getClipBounds();
        FontMetrics fm = g.getFontMetrics(font);
        int ascent = fm.getAscent();
        int descent = fm.getDescent();
        // 256 340 0 680
        int y = (clip.height - (ascent + descent)) / 2 + ascent;
        // 画出字符串
        IntStream.range(0, 6).forEach(i -> g.drawString(str, i * 680, y));
        g.dispose();
        // 绘制干扰线
        Random random = new Random();
        Graphics2D g2 = image.createGraphics();
        // 设置线条的颜色
        g2.setColor(VerifyCodeUtils.getRandColor(160, 200));
        IntStream.range(0, 20).map(i -> random.nextInt(width - 1)).forEach(x1 -> {
            int xl = random.nextInt(6) + 1;
            int yl = random.nextInt(12) + 1;
            // g2.drawLine(x1, y, x1 + xl + 40, y + yl + 20);
        });

        // 添加噪点
        float yawpRate = 0.05f;
        // 噪声率
        int area = (int)(yawpRate * width * height);
        IntStream.range(0, area).map(i -> random.nextInt(width)).forEach(x1 -> {
            int y1 = random.nextInt(height);
            int rgb = VerifyCodeUtils.getRandomIntColor();
            image.setRGB(x1, y1, rgb);
        });
        Color c = VerifyCodeUtils.getRandColor(200, 250);
        // 使图片扭曲
        // VerifyCodeUtils.shear(g2, width, height, c);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Encoder encoder = Base64.getEncoder();
        // 输出png图片
        ImageIO.write(image, "png", baos);
        byte[] bytes = baos.toByteArray();
        return encoder.encodeToString(bytes).trim();
    }

    // /**
    // * 测试
    // * @param args
    // * @throws IOException
    // */
    // public static void main(String[] args) throws IOException {
    // File dir = new File("D:/");
    // int w = 200, h = 80;
    // String verifyCode = generateVerifyCode(4);
    // File file = new File(dir, verifyCode + ".jpg");
    // outputImage(w, h, file, verifyCode);
    // }
}